
#include <gecode/driver.hh>

#include <gecode/int.hh>
#include <gecode/minimodel.hh>
#include <gecode/gist.hh>

#include "examples/scowl.hpp"
#include "./generatore.cpp"

using namespace Gecode;


// Grid data
namespace {
  // Grid data
  extern const int* grids[];
  // Number of grids
  extern const unsigned int n_grids;
}

class Crossword : public Script {
protected:
  // larghezza griglia
  const int w;
  // altezza griglia
  const int h;
  // Lettere possibili
  IntVarArray lettere;		//array di lunghezza variabile di lettere
public:
  /// Branching to use for model
  enum {
    BRANCH_WORDS,  // Branch sulle parole
    BRANCH_LETTERS // Branch sulle lettere
  };
  
  Crossword(const SizeOptions& options)	//i parametri passati sono relativi all'oggetto avente all'interno le opzioni
	: w(grids[options.size()][0]), 
	  h(grids[options.size()][1]),	
	  lettere ( *this, w*h ,'a','z') 	//intvarArray (home length min max)
      
      {
	
	// GRID compisizione
	/*	larghezza ,
	 *	altezza , 
	 * 	n= # neri ,
	 *	(x,y) x n, 
	 * 	[ 	lenght_w , 
	 * 		wd = # parole lunghe lenght_w,
	 *		(x_init , y_init , oriz/vert) ] per ogni possibili lunghezze  
	 * 
	 */
	
	
    //tale puntatore scorre le opzioni della grid grazie all'aritmetica dei puntatori
    
    const int* g = &grids[options.size()][2]; //puntatore *g = AL NUMERO DI CASELLE NERE

    // Matrix for letters
    Matrix<IntVarArray> matrice_griglia(lettere, w, h);

    // Set black fields to 0
    {
      IntVar black(*this,0,0);
      
      for (int n = *g++; n--; ) {  //*g++ = numero punti neri
	    int x=*g++;		//*g++ = x nero n
	    int y=*g++;		//*g++ = y nero n
				     
	    matrice_griglia(x,y) = black;	//posizionamento della casella nera sulla griglia
      }
    }

    // Array of all words
    IntVarArgs parole_set;

    /*Struttura informazione 
     * Precedenza alle parole più lunghe
	*  	A)lunghezza parola
	* 	B)numero di parole
	* 	c1) x parola 1
	* 	c2) y parola 2
	* 	c3) direzione parola 3
	*/
    
    // finche esistono lunghezze di parole non nulle
    while (int lunghezza_parole = *g++) {	// (A)
	// numero delle parole di tale lunghezza nel dizionario
	int n_parole_l_dict = dict.words(lunghezza_parole);
	// numero di parole di tale lunghezza nella griglia
	int n_parole_l_grid = *g++; // (B)

	if (n_parole_l_grid > n_parole_l_dict) 
	  fail();	// non ci sono abbastanza parole nel dizionario per riempire il cruciverba 
	else {
	    
	    //INIZIALIZZAZIONE ARRAY PAROLE
	    // array di delle parole indicizzate lunghe lunghezza_parole
	    IntVarArgs parole(*this,	n_parole_l_grid ,	0,	n_parole_l_dict-1); //words contiene di , 
						  // n elementi = (numero parole lunghe l necessarie) 
						  // elemento => indice della parola in dizionario lunga l
						  
	    parole_set << parole; //aggiungo parole al set globale (indici delle parole da considerare)
	    
	    // ALL DIFF
	    distinct(*this, parole, options.icl()); //tutte gli indici delle parole contenuti in words devono differire

	    //PROCESSO PAROLE lunghe l nella GRID
	    for (int pos_lettera=0;	 pos_lettera<lunghezza_parole;	 pos_lettera++) { //scorro le parole lettera per lettera
		
		IntSharedArray parole2lettere(n_parole_l_dict); // creazione di un array di LETTERE condiviso con le lettere in posizione pos_lettera
	// inizializzo la parola per la sua mappatura secondo la sua lettera
		
		//per ogni parola lunga lunghezza_parole ottengo la lettera nella sua posizione pos_lettera
		for (int i=n_parole_l_dict; i--; )	 
		    parole2lettere[i] = dict.word(lunghezza_parole,i)[pos_lettera];
		
		// collego la parola alle sue lettere
		for (int i=0; i<n_parole_l_grid; i++) {
		    // ottengo le coordinate della casella di partenza della parola sulla griglia
		    int x_pos = g[3*i+0];
		    int y_pos = g[3*i+1];
		    // ottengo la direzione della parola (orizzontale/verticale)
		    bool h_direct=(g[3*i+2] == 0);
		    // Vincolo che lega le lettere alle lettere delle parole
		    element(	*this,	parole2lettere,
					parole[i],
					h_direct ? 
						matrice_griglia(x_pos+pos_lettera,y_pos) :
						matrice_griglia(x_pos,y_pos + pos_lettera)	); 
		  }
	    }
	    // salto alle parole di lunghezza successiva
	    g += 3*n_parole_l_grid;
      }
    }
    
    switch (options.branching()) {
    case BRANCH_WORDS:
      // Branch by assigning words
      branch(*this, parole_set, INT_VAR_SIZE_AFC_MIN, INT_VAL_SPLIT_MIN);
      break;
    case BRANCH_LETTERS:
      // Branch by assigning letters
      branch(*this, lettere, INT_VAR_SIZE_AFC_MIN, INT_VAL_MIN);
      break;
    }
  }
  /// Constructor for cloning 
  Crossword(bool share, Crossword& s) 
    : Script(share,s), w(s.w), h(s.h) {
    lettere.update(*this, share, s.lettere);
  }
  /// Copy during cloning
  virtual Space*
  copy(bool share) {
    return new Crossword(share,*this);
  }
  /// Print solution
  virtual void
  print(std::ostream& os) const {
    // Matrix for letters
    Matrix<IntVarArray> matrice_griglia(lettere, w, h);
    
    //separazione orizzontale
	os<<'\t'<<" ";
	for(int m=0;m<w;m++)
	    os<<"----";
	os << std::endl ;
	    
    for (int i=0; i<h; i++) {
      os << '\t';
      for (int j=0; j<w; j++)
        if (matrice_griglia(j,i).assigned())
          if (matrice_griglia(j,i).val() == 0)
            os <<" | "<<"%";
          else
            os << " | "<<static_cast<char>(matrice_griglia(j,i).val());
        else
          os << '?';
      os << " |"<<std::endl;
      //separazione orizzontale
	os<<'\t'<<" ";
	for(int m=0;m<w;m++)
	    os<<"----";
	os << std::endl ;
    }
    os << std::endl << std::endl;
  }
  
 
};


int main(int argc, char* argv[]) {
  FileSizeOptions opt("Crossword");
//genero un array di tipo grids con la sola griglia che ho generato
//mantenendo la struttura usata precedentemente
int 	w=20,w_tmp,
	h=20,h_tmp,
	blk=120,blk_tmp;
cout<<"inserisci width (default "<<w<<") :"<<endl;
cin>>w_tmp;
if(w_tmp>1 && w_tmp<100)
	w=w_tmp;

cout<<"inserisci height (default "<<h<<") :"<<endl;
cin>>h_tmp;
if(h_tmp>1 && h_tmp<100)
	h=h_tmp;

cout<<"inserisci black (default "<<blk<<") :"<<endl;
cin>>blk_tmp;
if(blk_tmp>1 && blk_tmp<100)
	blk=blk_tmp;
  
cout<<"generazione crossword ( "<<w<<", "<<h<<", "<<blk<<" )"<<endl;

Generatore gen(w,h,blk); 
  
  grids[0]= gen.genera();    
  opt.size(0);
  opt.icl(ICL_VAL);
  opt.parse(argc,argv);
  dict.init(opt.file());
  
  //run con branch sulle praole

  opt.branching(Crossword::BRANCH_WORDS);
cout<<"branch parole DFS"<<endl;
  Script::run<Crossword,BAB,SizeOptions>(opt);
  
int x;
cin>>x;

  //run con branch sulle lettere
cout<<"branch lettere DFS"<<endl;
 //opt.branching(Crossword::BRANCH_LETTERS);
  Script::run<Crossword,DFS,SizeOptions>(opt);
  

  
  //run con branch sulle lettere
  //opt.branching(Crossword::BRANCH_WORDS);
  //Script::run<Crossword,BAB,SizeOptions>(opt);
    
  //Crossword* k= new Crossword(opt);
  //Gist::dfs(k);
  //delete k;
  return 0;
}

namespace {

  

  const int* grids[] = {};

  const unsigned int n_grids = 1;

}

// STATISTICS: example-any
